use std::cell::RefCell;
use std::rc::Rc;

use crate::instruction::instruction::Instruction;
use crate::vm_thread::{VMFrame, VMThread};
use crate::vm_val::{NULL_REF, VMValEnum, VMValInteger};

pub struct if_icmpeq {
    pub offset: u16,
}

impl Instruction for if_icmpeq {
    fn read_operands(&mut self, frame: Rc<RefCell<VMFrame>>) {
        self.offset = frame.borrow_mut().read_u16();
    }

    fn exec(&mut self, frame: Rc<RefCell<VMFrame>>, thread: &VMThread) {
        let operand = read_operand(frame.clone());
        if operand.0 == operand.1 {
            let mut rw_frame = frame.borrow_mut();
            rw_frame.pc = rw_frame.pc - 3 + (self.offset as usize);
        }
    }

    fn to_string(&self) -> String {
        String::from("if_icmpeq")
    }
}


pub struct if_icmpne {
    pub offset: u16,
}

impl Instruction for if_icmpne {
    fn read_operands(&mut self, frame: Rc<RefCell<VMFrame>>) {
        self.offset = frame.borrow_mut().read_u16();
    }

    fn exec(&mut self, frame: Rc<RefCell<VMFrame>>, thread: &VMThread) {
        let operand = read_operand(frame.clone());
        if operand.0 != operand.1 {
            let mut rw_frame = frame.borrow_mut();
            rw_frame.pc = rw_frame.pc - 3 + (self.offset as usize);
        }
    }

    fn to_string(&self) -> String {
        String::from("if_icmpne")
    }
}


pub struct if_icmplt {
    pub offset: u16,
}

impl Instruction for if_icmplt {
    fn read_operands(&mut self, frame: Rc<RefCell<VMFrame>>) {
        self.offset = frame.borrow_mut().read_u16();
    }

    fn exec(&mut self, frame: Rc<RefCell<VMFrame>>, thread: &VMThread) {
        let operand = read_operand(frame.clone());
        if operand.0 < operand.1 {
            let mut rw_frame = frame.borrow_mut();
            rw_frame.pc = rw_frame.pc - 3 + (self.offset as usize);
        }
    }

    fn to_string(&self) -> String {
        String::from("if_icmplt")
    }
}


pub struct if_icmpge {
    pub offset: u16,
}

impl Instruction for if_icmpge {
    fn read_operands(&mut self, frame: Rc<RefCell<VMFrame>>) {
        self.offset = frame.borrow_mut().read_u16();
    }

    fn exec(&mut self, frame: Rc<RefCell<VMFrame>>, thread: &VMThread) {
        let operand = read_operand(frame.clone());
        if operand.0 >= operand.1 {
            let mut rw_frame = frame.borrow_mut();
            rw_frame.pc = rw_frame.pc - 3 + (self.offset as usize);
        }
    }

    fn to_string(&self) -> String {
        String::from("if_icmpge")
    }
}


pub struct if_icmpgt {
    pub offset: u16,
}

impl Instruction for if_icmpgt {
    fn read_operands(&mut self, frame: Rc<RefCell<VMFrame>>) {
        self.offset = frame.borrow_mut().read_u16();
    }

    fn exec(&mut self, frame: Rc<RefCell<VMFrame>>, thread: &VMThread) {
        let operand = read_operand(frame.clone());
        if operand.0 > operand.1 {
            let mut rw_frame = frame.borrow_mut();
            rw_frame.pc = rw_frame.pc - 3 + (self.offset as usize);
        }
    }

    fn to_string(&self) -> String {
        String::from("if_icmpgt")
    }
}


pub struct if_icmple {
    pub offset: u16,
}

impl Instruction for if_icmple {
    fn read_operands(&mut self, frame: Rc<RefCell<VMFrame>>) {
        self.offset = frame.borrow_mut().read_u16();
    }

    fn exec(&mut self, frame: Rc<RefCell<VMFrame>>, thread: &VMThread) {
        let operand = read_operand(frame.clone());
        if operand.0 <= operand.1 {
            let mut rw_frame = frame.borrow_mut();
            rw_frame.pc = rw_frame.pc - 3 + (self.offset as usize);
        }
    }

    fn to_string(&self) -> String {
        String::from("if_icmple")
    }
}


fn read_operand(frame: Rc<RefCell<VMFrame>>) -> (i32, i32) {
    let rw_frame = frame.borrow_mut();
    let mut rw_operand_stack = rw_frame.operand_stack.borrow_mut();
    let v2 = rw_operand_stack.pop().unwrap();
    let v1 = rw_operand_stack.pop().unwrap();
    let i1 = VMValEnum::convert2integer(&v1);
    let i2 = VMValEnum::convert2integer(&v2);
    return (i1.num, i2.num);
}


pub struct ifeq {
    pub offset: u16,
}

impl Instruction for ifeq {
    fn read_operands(&mut self, frame: Rc<RefCell<VMFrame>>) {
        self.offset = frame.borrow_mut().read_u16();
    }

    fn exec(&mut self, frame: Rc<RefCell<VMFrame>>, thread: &VMThread) {
        let rw_frame = frame.borrow_mut();
        let mut rw_operand_stack = rw_frame.operand_stack.borrow_mut();
        let val = rw_operand_stack.pop().unwrap();
        let num = match val {
            VMValEnum::VMValByte { val } => {
                val.num as i32
            }
            VMValEnum::VMValShort { val } => {
                val.num as i32
            }
            VMValEnum::VMValInteger { val } => {
                val.num as i32
            }
            VMValEnum::VMValBoolean { val } => {
                if val.val { 1 } else { 0 }
            }
            _ => {
                panic!("{},{},{},{:#?}", file!(), line!(), "类型转化异常", val);
            }
        };

        if num == 0 {
            let mut rw_frame = frame.borrow_mut();
            rw_frame.pc = rw_frame.pc - 1 + (self.offset as usize);
        }
    }

    fn to_string(&self) -> String {
        String::from("ifeq")
    }
}


pub struct ifne {
    pub offset: u16,
}

impl Instruction for ifne {
    fn read_operands(&mut self, frame: Rc<RefCell<VMFrame>>) {
        self.offset = frame.borrow_mut().read_u16();
    }

    fn exec(&mut self, frame: Rc<RefCell<VMFrame>>, thread: &VMThread) {
        let rw_frame = frame.borrow_mut();
        let mut rw_operand_stack = rw_frame.operand_stack.borrow_mut();
        let val = rw_operand_stack.pop().unwrap();
        let num = match val {
            VMValEnum::VMValByte { val } => {
                val.num as i32
            }
            VMValEnum::VMValShort { val } => {
                val.num as i32
            }
            VMValEnum::VMValInteger { val } => {
                val.num as i32
            }
            VMValEnum::VMValBoolean { val } => {
                if val.val { 1 } else { 0 }
            }
            _ => {
                panic!("{},{},{},{:#?}", file!(), line!(), "类型转化异常", val);
            }
        };
        if num != 0 {
            let mut rw_frame = frame.borrow_mut();
            rw_frame.pc = rw_frame.pc - 1 + (self.offset as usize);
        }
    }

    fn to_string(&self) -> String {
        String::from("ifne")
    }
}


pub struct iflt {
    pub offset: u16,
}

impl Instruction for iflt {
    fn read_operands(&mut self, frame: Rc<RefCell<VMFrame>>) {
        self.offset = frame.borrow_mut().read_u16();
    }

    fn exec(&mut self, frame: Rc<RefCell<VMFrame>>, thread: &VMThread) {
        let rw_frame = frame.borrow_mut();
        let mut rw_operand_stack = rw_frame.operand_stack.borrow_mut();
        let val = rw_operand_stack.pop().unwrap();
        let num = match val {
            VMValEnum::VMValByte { val } => {
                val.num as i32
            }
            VMValEnum::VMValShort { val } => {
                val.num as i32
            }
            VMValEnum::VMValInteger { val } => {
                val.num as i32
            }
            VMValEnum::VMValBoolean { val } => {
                if val.val { 1 } else { 0 }
            }
            _ => {
                panic!("{},{},{},{:#?}", file!(), line!(), "类型转化异常", val);
            }
        };
        if num < 0 {
            let mut rw_frame = frame.borrow_mut();
            rw_frame.pc = rw_frame.pc - 1 + (self.offset as usize);
        }
    }

    fn to_string(&self) -> String {
        String::from("iflt")
    }
}


pub struct ifle {
    pub offset: u16,
}

impl Instruction for ifle {
    fn read_operands(&mut self, frame: Rc<RefCell<VMFrame>>) {
        self.offset = frame.borrow_mut().read_u16();
    }

    fn exec(&mut self, frame: Rc<RefCell<VMFrame>>, thread: &VMThread) {
        let rw_frame = frame.borrow_mut();
        let mut rw_operand_stack = rw_frame.operand_stack.borrow_mut();
        let val = rw_operand_stack.pop().unwrap();
        let num = match val {
            VMValEnum::VMValByte { val } => {
                val.num as i32
            }
            VMValEnum::VMValShort { val } => {
                val.num as i32
            }
            VMValEnum::VMValInteger { val } => {
                val.num as i32
            }
            VMValEnum::VMValBoolean { val } => {
                if val.val { 1 } else { 0 }
            }
            _ => {
                panic!("{},{},{},{:#?}", file!(), line!(), "类型转化异常", val);
            }
        };
        if num <= 0 {
            let mut rw_frame = frame.borrow_mut();
            rw_frame.pc = rw_frame.pc - 1 + (self.offset as usize);
        }
    }

    fn to_string(&self) -> String {
        String::from("ifle")
    }
}


pub struct ifgt {
    pub offset: u16,
}

impl Instruction for ifgt {
    fn read_operands(&mut self, frame: Rc<RefCell<VMFrame>>) {
        self.offset = frame.borrow_mut().read_u16();
    }

    fn exec(&mut self, frame: Rc<RefCell<VMFrame>>, thread: &VMThread) {
        let rw_frame = frame.borrow_mut();
        let mut rw_operand_stack = rw_frame.operand_stack.borrow_mut();
        let val = rw_operand_stack.pop().unwrap();
        let num = match val {
            VMValEnum::VMValByte { val } => {
                val.num as i32
            }
            VMValEnum::VMValShort { val } => {
                val.num as i32
            }
            VMValEnum::VMValInteger { val } => {
                val.num as i32
            }
            VMValEnum::VMValBoolean { val } => {
                if val.val { 1 } else { 0 }
            }
            _ => {
                panic!("{},{},{},{:#?}", file!(), line!(), "类型转化异常", val);
            }
        };
        if num > 0 {
            let mut rw_frame = frame.borrow_mut();
            rw_frame.pc = rw_frame.pc - 1 + (self.offset as usize);
        }
    }

    fn to_string(&self) -> String {
        String::from("ifgt")
    }
}


pub struct ifge {
    pub offset: u16,
}

impl Instruction for ifge {
    fn read_operands(&mut self, frame: Rc<RefCell<VMFrame>>) {
        self.offset = frame.borrow_mut().read_u16();
    }

    fn exec(&mut self, frame: Rc<RefCell<VMFrame>>, thread: &VMThread) {
        let rw_frame = frame.borrow_mut();
        let mut rw_operand_stack = rw_frame.operand_stack.borrow_mut();
        let val = rw_operand_stack.pop().unwrap();
        let num = match val {
            VMValEnum::VMValByte { val } => {
                val.num as i32
            }
            VMValEnum::VMValShort { val } => {
                val.num as i32
            }
            VMValEnum::VMValInteger { val } => {
                val.num as i32
            }
            VMValEnum::VMValBoolean { val } => {
                if val.val { 1 } else { 0 }
            }
            _ => {
                panic!("{},{},{},{:#?}", file!(), line!(), "类型转化异常", val);
            }
        };
        if num >= 0 {
            let mut rw_frame = frame.borrow_mut();
            rw_frame.pc = rw_frame.pc - 1 + (self.offset as usize);
        }
    }

    fn to_string(&self) -> String {
        String::from("ifge")
    }
}


pub struct ifnonnull {
    pub offset: u16,
}

impl Instruction for ifnonnull {
    fn read_operands(&mut self, frame: Rc<RefCell<VMFrame>>) {
        self.offset = frame.borrow_mut().read_u16();
    }

    fn exec(&mut self, frame: Rc<RefCell<VMFrame>>, thread: &VMThread) {
        let rw_frame = frame.borrow_mut();
        let mut rw_operand_stack = rw_frame.operand_stack.borrow_mut();
        let val = rw_operand_stack.pop().unwrap();
        if VMValEnum::convert2ref(&val).num != NULL_REF.num {
            let mut rw_frame = frame.borrow_mut();
            rw_frame.pc = rw_frame.pc - 1 + (self.offset as usize);
        }
    }

    fn to_string(&self) -> String {
        String::from("ifnonnull")
    }
}